﻿// -----------------------------------------------------------------------
// <copyright file="NoiseAdded.cs" company="fcis">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace IP_Package.Operations.Restoration.Add_noise
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class NoiseAdded : IPPackage.ImageOperation.Open 
    {
        /// <summary>
        /// object to use comman methods insert HistoOperation
        /// </summary>
        private Global_applications.Global_methods_user useMethods = new Global_applications.Global_methods_user();

        /// <summary>
        /// value Of Image Plus Noise Value
        /// </summary>
        private double[,,] valueOfImagePlusNoiseValue;

        /// <summary>
        /// Adds the salt and pepper noise.
        /// </summary>
        /// <param name="probOfSalt">The prob of salt happened.</param>
        /// <param name="probOfPepper">The prob of pepper happend.</param>
        /// <returns>new image</returns>
        public Bitmap AddSaltAndPepperNoise(float probOfSalt, float probOfPepper)
        {
            Bitmap newImag = new Bitmap(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
            Global_applications.Random_Generate newRand = new Global_applications.Random_Generate(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
            int numberOfPixelSalt = (int)Math.Floor((double)this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1) * probOfSalt);
            this.ChangeValueOfBuffer(numberOfPixelSalt, 255);
            int numberOfPixelPepper = (int)Math.Floor((double)this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1) * probOfPepper);
            this.ChangeValueOfBuffer(numberOfPixelPepper, 0);
            for (int j = 0; j < this.BufferOfImage.GetLength(0); j++)
            {
                for (int k = 0; k < this.BufferOfImage.GetLength(1); k++)
                {
                    newImag.SetPixel(j, k, this.BufferOfImage[j, k]);
                }
            }

                return newImag;
        }

        /// <summary>
        /// Adds the uniform noise.
        /// </summary>
        /// <param name="a">start of uniform noise value.</param>
        /// <param name="b">end of uniform noise value.</param>
        /// <param name="precentage">The precentage of noise.</param>
        /// <returns>the new image</returns>
        public Bitmap AddUniformNoise(int a, int b, float precentage)
        {
            int numberOfNoisePixel = (int)Math.Floor((double)(1.0 / (b - a)) * this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1) * precentage);
            this.ChangeValueOfBuffer(numberOfNoisePixel, a, b);
            this.useMethods.BufferOfImage = this.BufferOfImage;
            Bitmap resultImg = this.useMethods.Normalization(this.valueOfImagePlusNoiseValue);
            this.NewBufferOfImage = this.useMethods.NewBufferOfImage;
            return resultImg;
        }

        /// <summary>
        /// Adds the gaussian noise.
        /// </summary>
        /// <param name="mean">The mean of noise.</param>
        /// <param name="sigma">The sigma of noise.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        /// <returns>new image</returns>
        public Bitmap AddGaussianNoise(float mean, float sigma, float precentage)
        {
            Bitmap newImag = new Bitmap(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
            this.ChangeValueOfBuffer(mean, sigma, precentage);
            this.useMethods.BufferOfImage = this.BufferOfImage;
            Bitmap resultImg = this.useMethods.Normalization(this.valueOfImagePlusNoiseValue);
            this.NewBufferOfImage = this.useMethods.NewBufferOfImage;
            return resultImg;
        }

        /// <summary>
        /// Adds the periodic noise.
        /// </summary>
        /// <param name="ampitude">The ampitude.</param>
        /// <param name="freX">The fre X.</param>
        /// <param name="freY">The fre Y.</param>
        /// <param name="phX">The ph X.</param>
        /// <param name="phY">The ph Y.</param>
        /// <returns>noise image</returns>
        public Bitmap AddPeriodicNoise(float ampitude, float freX, float freY, float phX, float phY)
        {
            double[,,] valueOFImageWithNoise = new double[this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1), 3];
            for (int i = 0; i < this.BufferOfImage.GetLength(1); i++)
            {
                for (int j = 0; j < this.BufferOfImage.GetLength(0); j++)
                {
                    valueOFImageWithNoise[j, i, 0] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.BufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.BufferOfImage.GetLength(1)) + phY)) + this.BufferOfImage[j, i].R;
                    valueOFImageWithNoise[j, i, 1] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.BufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.BufferOfImage.GetLength(1)) + phY)) + this.BufferOfImage[j, i].G;
                    valueOFImageWithNoise[j, i, 2] = (ampitude * Math.Sin(((2 * Math.PI * j * freX) / this.BufferOfImage.GetLength(0)) + phX + ((2 * Math.PI * i * freY) / this.BufferOfImage.GetLength(1)) + phY)) + this.BufferOfImage[j, i].B;
                }
            }

            Global_applications.Global_methods_user method = new Global_applications.Global_methods_user();
            Bitmap result = method.Normalization(valueOFImageWithNoise);
            this.NewBufferOfImage = method.NewBufferOfImage;
            return result;
        }

        /// <summary>
        /// Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="numberOfPixelChange">The number of pixel change.</param>
        /// <param name="newValue">The new value of this pixel.</param>
        private void ChangeValueOfBuffer(int numberOfPixelChange, int newValue)
    {
        Random newRand = new Random();
            IP_Package.Global_applications.Random_Generate rand = new Global_applications.Random_Generate(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
        for (int i = 0; i < numberOfPixelChange; i++) 
            {
                Position randomValue = rand.NextPerfectRandom(newRand);
                this.BufferOfImage[randomValue.X, randomValue.Y] = Color.FromArgb(newValue, newValue, newValue);
            }
    }

        /// <summary>
        /// Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="numberOfPixelChange">The number of pixel change.</param>
        /// <param name="a">start of uniform noise value.</param>
        /// <param name="b">end of uniform noise value.</param>
        private void ChangeValueOfBuffer(int numberOfPixelChange, int a, int b)
        {
            IP_Package.Global_applications.Random_Generate rand = new Global_applications.Random_Generate(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1), 3];
            Random newRand = new Random();
            for (int j = a; j < b; j++)
            {
                for (int i = 0; i < numberOfPixelChange; i++)
                {
                    Position randomValue = rand.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.BufferOfImage[randomValue.X, randomValue.Y].R + j;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.BufferOfImage[randomValue.X, randomValue.Y].G + j;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.BufferOfImage[randomValue.X, randomValue.Y].B + j;
                 }
            }

            for (int i = 0; i < (this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1)) - (numberOfPixelChange * (b - a)); i++)
            {
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 0] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 1] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 2] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].B;        
            }
        }

        /// <summary>
        ///  Changes the value of buffer to the new image.
        /// </summary>
        /// <param name="mean">The mean of noise.</param>
        /// <param name="sigma">The sigma of noise.</param>
        /// <param name="precentage">The precentage of noise add.</param>
        private void ChangeValueOfBuffer(float mean, float sigma, float precentage)
        {
            int allNumberOfPixelChange = 0;
            Random newRand = new Random();
            IP_Package.Global_applications.Random_Generate rand = new Global_applications.Random_Generate(this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1));
            this.valueOfImagePlusNoiseValue = new double[this.BufferOfImage.GetLength(0), this.BufferOfImage.GetLength(1), 3];
            for (int i = 0; i < 256; i++)
            {
                int numberOfPixel = (int)((1.0 / (Math.Sqrt(2 * Math.PI) * sigma)) * Math.Exp(-.5 * Math.Pow((i - mean) / sigma, 2)) * this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1) * precentage);
                for (int j = 0; j < numberOfPixel; j++) 
                {
                    Position randomValue = rand.NextPerfectRandom(newRand);
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 0] = this.BufferOfImage[randomValue.X, randomValue.Y].R + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 1] = this.BufferOfImage[randomValue.X, randomValue.Y].G + i;
                    this.valueOfImagePlusNoiseValue[randomValue.X, randomValue.Y, 2] = this.BufferOfImage[randomValue.X, randomValue.Y].B + i;
                }

                allNumberOfPixelChange += numberOfPixel;
            }

            for (int i = 0; i < (this.BufferOfImage.GetLength(0) * this.BufferOfImage.GetLength(1)) - allNumberOfPixelChange; i++)
            {
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 0] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].R;
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 1] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].G;
                this.valueOfImagePlusNoiseValue[rand.RandArray[i].X, rand.RandArray[i].Y, 2] = this.BufferOfImage[rand.RandArray[i].X, rand.RandArray[i].Y].B;
            }
        }
    }
}
